
   /* Globals */
   var cblock = {connected: false, conid: ""}; /* Connection Block */


   /* exception handler */

   handleGenericException = function(res)
   {
     if (res === undefined)
       return false;
   
     /* special exception not handled here */ 
     if (res.sequence !== undefined)
       return true;

     return handleException(res);
   }

   handleException = function(res)
   {
     if (!(res && res.exception))
       return false;
      
     if (res.cause)
       alert(res.cause);
     else
       alert(res.exception+": "+res.message);

     return true;
   };



   /* callback manager */
   var callback_map = {};
   var event_callback_map = {};

   set_event_callback = function(name, callback)
   {
     if(event_callback_map[name] == undefined)
       event_callback_map[name] = [];

     event_callback_map[name].push(callback);
   }

   unset_event_callback = function(name, callback)
   {
     if (event_callback_map[name] == undefined)
       return;

     for (var i=0; i < event_callback_map[name].length; i++)
     {
        if (event_callback_map[i] == callback)
        {
          delete event_callback_map[index];
          return;
        }
     } 
   }

   get_event_callback = function(name)
   {
     var all = [];
     if (event_callback_map["*"])
       all = event_callback_map["*"];

     var list = event_callback_map[name];

     if (list && list.length > 0)
       return all.concat(list);
     else if (all.length > 0)
       return all; 

     return undefined;
   }

   invoke_event_callback = function(name, result)
   {
     var callback_list = get_event_callback(name);
     if (callback_list) 
     {
       for( var i=0; i < callback_list.length; i++)
       {
         callback_list[i](result);
       }
     }
   }

   set_callback = function(sequence, callback)
   {
     callback_map[sequence] = callback;
   };

   unset_callback = function(sequence)
   {
     if(callback_map[sequence])
       delete callback_map[sequence];
   };

   get_callback = function(sequence)
   {
     if (callback_map[sequence])
       return callback_map[sequence];
     return undefined;
   };

   invoke_callback = function(sequence, result)
   {
     var callback = get_callback(sequence);
     if (callback)
     {
       callback(result);
       unset_callback(sequence);
     }
   }
  


   /* packet manager */
   receive_packet = function(resultText)
   {
     //alert(resultText);
     var result = JSON.parse(resultText);
     if (handleGenericException(result))
     {
       if (result.sequence !== undefined)
         invoke_callback(result.sequence, result); 
       return;
     }

     var sequence = result.data[0];
     invoke_callback(result.data[0], result);
     invoke_event_callback(result.data[1], result);
   };

  
   send_packet = function(packet, callback)
   {
     if (packet.conid === undefined)
       throw new Error("conid argument required");
     
     if(packet.words === undefined)
       throw new Error("words argument required");

     var applet = $("#client_applet").get(0);
    
     var result = JSON.parse(applet.send(JSON.stringify(packet)));

     if (handleGenericException(result))
       return;

     callback_map[result.data] = callback

     return result;
   };

  make_connection = function(data, callback)
  {
    var applet = $("#client_applet").get(0);
    
    data.cb = "receive_packet";
    
    set_callback("0", callback);

    var res = JSON.parse(applet.connect(JSON.stringify(data)));
  
    if (handleGenericException(res))
      return;

    return res.data;
  }





    $(document).ready(function()
     { 
    dojo.require("dijit.form.Button");
    dojo.require("dijit.Dialog");
    dojo.require("dijit.TooltipDialog");
    dojo.require("dijit.Tooltip");
    dojo.require("dijit.form.TextBox");
    dojo.require("dijit.form.DateTextBox");
    dojo.require("dijit.form.TimeTextBox");
    dojo.require("dijit.dijit");
    dojo.require("dojox.layout.TableContainer");
    dojo.require("dijit.layout.BorderContainer");
    dojo.require("dijit.layout.TabContainer");
    dojo.require("dijit.layout.LayoutContainer");
    dojo.require("dijit.form.TextBox");
    dojo.require("dijit.form.CheckBox");
    dojo.require("dijit.form.NumberSpinner");
    dojo.require("dijit.MenuBar");
    dojo.require("dijit.MenuBarItem");
    dojo.require("dijit.PopupMenuBarItem");
    dojo.require("dijit.Menu");
    dojo.require("dijit.MenuItem");
    dojo.require("dijit.CheckedMenuItem");
    dojo.require("dijit.PopupMenuItem");
    dojo.require("dijit.form.Textarea");
    dojo.require("dijit.form.SimpleTextarea");
    dojo.require("dijit.form.Form");

         var applet = $("#client_applet").get(0);

         /* make the login dialog */

         var dlg = new dijit.Dialog(
         {
           title: "BFBC2 Web Admin Login",
           style: "width: 225px; padding: 0px;",
           modal: true
         });

         var dlgContainer = new dojox.layout.TableContainer(
         {
           cols:1,
           style: "width: 200px; padding: 0px",
           spacing: "5px"
         });

         var address_txt = new dijit.form.TextBox(
         {
           style: "width: 115px; padding-left: 5px",
           label: "Address:",
           value: ""
         });

         var address_tt =  new dijit.Tooltip(
         {
           connectId:[address_txt.domNode], 
           label: "This is the IP address or host name of the BFBC2 server."
         });

         var port_txt = new dijit.form.TextBox(
         {
           style: "width: 115px; padding-left: 5px",
           value: "48888",
           label: "Port:" 
         });


         var port_tt =  new dijit.Tooltip(
         {
           connectId:[port_txt.domNode], 
           label: "This is the port number for BFBC2 server."
         });

         var user_txt = new dijit.form.TextBox(
         {
           label: "Username:",
           style: "width: 115px; padding-left: 5px",
           disabled: true,
           value: "admin"
         });


         var user_tt =  new dijit.Tooltip(
         {
           connectId:[user_txt.domNode], 
           label: "This is the user name if connecting to PRoCon layer server. <br /> (PRoCon layer connection is not currently supported)"
         });

         var pass_txt = new dijit.form.TextBox(
         {
           type: "password",
           style: "width: 115px; padding-left: 5px",
           label: "Password:" 
         });

         var login_flds = [address_txt, port_txt , pass_txt];

         /* enable autocompelte */
         for(var i in login_flds) { $(login_flds[i].domNode).attr("autocomplete", "on");}


         var connect_btn = new dijit.form.Button(
         {
           label: "Connect",
           type: "submit",
           onClick: function()
           {
             var btn = this;
             
             /* disbable the fields */
             for(var i in login_flds){ login_flds[i].set("disabled", true)};
             btn.set("disabled", true);

             var res = make_connection(
             {
               user: user_txt.attr("value"),
               pass: pass_txt.attr("value"),
               host: address_txt.attr("value"),
               port: parseInt(port_txt.attr("value"))
             }, 
            /* connection callback */
            function(result)
             {
               /* re-enable the fields */
               for(i in login_flds){ login_flds[i].set("disabled", false)};
               btn.set("disabled", false);

               if (handleException(result))
                 return;

               cblock.connected = true;
               if (result.data[1] == "ConnectionID")
                 cblock.conid = result.data[2];
               else
                 alert("ConnectionID not received!");

               dlg.hide();

               /* this is so that we can call the function and still have access to the UI elements */
               eval("client_start()"); 

             });



           }
         });

         dojo.connect(dlg, 'onHide', function() {$(".dijitTooltip").hide();});

         dlgContainer.addChild(address_txt);
         dlgContainer.addChild(port_txt);
         dlgContainer.addChild(user_txt);
         dlgContainer.addChild(pass_txt);
         dlgContainer.addChild(new dijit.layout.ContentPane({style: "padding: 0px;", content:connect_btn}));

         var container = new dijit.layout.ContentPane(
         {
           content: dlgContainer
         });

         dlg.setContent(container);



        /* Main Container */
        var bc = new dijit.layout.BorderContainer(
        {
            style: "width: 100%; height: 100%; border: 0px; padding: 0px",
            border: false,
            gutter:'false'
        });


        var pMenuBar = new dijit.MenuBar({});
        var pSubMenu = new dijit.Menu({});

        var context_menu = new dijit.PopupMenuBarItem({label: "Context"});

        pMenuBar.addChild(context_menu);

        pSubMenu.addChild(new dijit.MenuItem({
            label: "Disconnect",
            onClick: function() { location.reload(); }
        }));

        var pSubMenu2 = new dijit.Menu({});

        pSubMenu2.addChild(new dijit.MenuItem({
            label: "About"
        }));

        var optionsBarItem = new dijit.PopupMenuBarItem({
            label: "Options",
            popup: pSubMenu
        });

        pMenuBar.addChild(optionsBarItem);

        pMenuBar.addChild(new dijit.PopupMenuBarItem({
            label: "Help",
            popup: pSubMenu2
        }));
     

        var ribbonContainer = new dijit.layout.BorderContainer({gutter: false, region: "top", style: "height: 60px; border: 0px;padding-top:0px;"});
        var infoRibbon = new dojox.layout.TableContainer({region: "center", rows: 1, cols: 7, content:"left ribbon pane", style: "padding-top: 0px;padding-left: 10px;border:0px;", labelWidth: 45});
        var menuRibbonPane = new dijit.layout.ContentPane({region: "right", content: pMenuBar, style: "padding-right: 50px; background-color: transparent; border: 0px; padding-top: 0px"});
        ribbonContainer.addChild(menuRibbonPane);
        ribbonContainer.addChild(infoRibbon);

        //create the labels and fields
        var label_css = "font-weight: bold; overflow: hidden; white-space: nowrap;";
        var server_txt = new dijit.layout.ContentPane({style:label_css, label: "Server: ", content: "Battlefield Bad Company 2 Server Name"});
        var players_txt = new dijit.layout.ContentPane({style:label_css, label: "Players: ", content: "0/0"});
        var mode_txt = new dijit.layout.ContentPane({style:label_css, label: "Mode: ", content: "Game Mode"});
        var version_txt = new dijit.layout.ContentPane({style:label_css, label: "Version: ", content: "Server Version"});
        var map_txt = new dijit.layout.ContentPane({style:label_css, label: "Map: ", content: "Map Name"});
        var time_txt = new dijit.layout.ContentPane({style:label_css, label: "Time: ", content: "Local Time"});
        var rounds_txt = new dijit.layout.ContentPane({style:label_css, label: "Rounds: ", content: "0/0"});

        infoRibbon.addChild(server_txt);
        infoRibbon.addChild(mode_txt);
        infoRibbon.addChild(map_txt);
        infoRibbon.addChild(rounds_txt);
        infoRibbon.addChild(players_txt);
        infoRibbon.addChild(version_txt);
        infoRibbon.addChild(time_txt);
        

 
        // make the tabs
        var tabsContainer = new dijit.layout.TabContainer({region: "center", style:"border-right: 0px"});

        //make the console
        var console_css = "font-family:Lucida Console, Andale Mono, Consolas, Monaco, monospace; font-size: 1.1em";
        var consoleContainer = new dijit.layout.BorderContainer({style: "border: 0px"});
        var consoleTabPane = new dijit.layout.ContentPane({title: "Console"}); 
        var consoleTop = new dijit.layout.ContentPane({ style: console_css, region:"center", content: "", gutter: false});
        var consoleBottom = new dijit.layout.ContentPane({style: "padding: 0px;", region:"bottom", content:"bottom", splitter: true});

        var consoleTextArea = new dijit.form.SimpleTextarea({style: console_css+";width: 99%; height: 99%; border: 0px"});
        consoleBottom.setContent(consoleTextArea);


        consoleContainer.addChild(consoleTop);
        consoleContainer.addChild(consoleBottom);
        consoleTabPane.setContent(consoleContainer);
  
        tabsContainer.addChild(consoleTabPane);

        var under_construction = [ "Players", "Chat", "Events", "Lists", "Server Settings"]
        var index;
        for (index in under_construction)
        {
          var title = under_construction[index];
          var tab = new dijit.layout.ContentPane({"title": title, style: "background-color: #cccccc", content: ""});
          tabsContainer.addChild(tab);
      
        }


        bc.addChild(ribbonContainer);
        bc.addChild(tabsContainer);

        dojo.body().appendChild(bc.domNode);
        bc.startup();

        /* this is a hack, to add the form tag */
        var tbl = dlgContainer.domNode;
        var prnt = tbl.parentNode;
        prnt.removeChild(tbl);
        var frm = $("<form></form>");
        $(frm).append(tbl);
        $(prnt).append(frm);
        


        tabsContainer.selected_tab = consoleTabPane;


        /* Make global Object to Represent the state of the console */

        var packet2HTML = function(result, parent_class, child_class)
        {
          var words = result.data;
          if (parent_class === undefined)
            parent_class = "packet";

          if (child_class === undefined)
            child_class = "packet-word";

          str = "";
          for(var i = 1; i < words.length; i++)
            str += "<span class=\""+child_class+"\">"+words[i]+"</span>";

          str = "<div class=\""+parent_class+"\">"+ str+"</div>";

          return str;
        };

        cblock.console = {};
        cblock.console.autoscroll = true;
        cblock.console.showevents = true;
        cblock.console.textpane = consoleTop;
        cblock.console.textarea = consoleTextArea;
 
        cblock.console.add = function(data)
        {
           $(cblock.console.textpane.domNode).append(data);
           if (cblock.console.getAutoscroll())
             $(cblock.console.textpane.domNode).scrollTop(cblock.console.textpane.domNode.scrollHeight);
        };

        cblock.console.send = function()
        {
           var str = cblock.console.textarea.attr("value");

           /* remove non-printables */
           str = str.replace(/[^A-Za-z 0-9 \.,\?""!@#\$%\^&\*\(\)-_=\+;:<>\/\\\|\}\{\[\]`~]*/g, "");
          
           /* trim white spaces */
           str = str.replace(/^\s*/, '').replace(/\s*$/, '');
        
           str = str.replace("\\,", "&comma;");
           /* split at every comma not preceeded by \ */
           var words  = str.split(",");

           /* fix the commas that are escaped, and trim the words*/
           for(var i in words)
              words[i] = words[i].replace("&comma;", ",").replace(/^\s*/, '').replace(/\s*$/, '');

           var rs = send_packet({conid: cblock.conid, "words": words}, function(result)
           {
             cblock.console.add(packet2HTML(result, "packet-response"));
           });

           cblock.console.add(packet2HTML({data:[rs.data].concat(words)}, "packet-client"));
        };

        cblock.console.event_listener = function(result)
        {
           if (!cblock.console.getShowEvents())
             return;
        
           var str = packet2HTML(result);
           cblock.console.add(str);
        };

        dojo.connect(cblock.console.textarea, "onKeyUp", function(e)
         {
           if (e.shiftKey &&  e.keyCode == dojo.keys.ENTER)
             return;
           else if (!e.shiftKey && e.keyCode == dojo.keys.ENTER)
           {
             cblock.console.send();
             return false;
           }
         });


        /* these two are to prevent the ENTER key event from bubbling */
        var shift_pressed = false;
        $(cblock.console.textarea.domNode).keyup(function(e)
        {
          if (e.keyCode == dojo.keys.SHIFT)
            shift_pressed = false;
        });

        $(cblock.console.textarea.domNode).keydown(function(e)
        { 
          if (e.keyCode == dojo.keys.SHIFT)
          {
            shift_pressed = true;
            return true;
          }
          else if (e.keyCode == dojo.keys.ENTER && !shift_pressed) 
            return false;

          return true;
        });

        cblock.console.setAutoscroll = function(state)
        {
          cblock.console.autoscroll = state;
        };

        cblock.console.getAutoscroll = function (){return cblock.console.autoscroll;}

        cblock.console.setShowEvents = function(state)
        {
          cblock.console.showevents = state;
        };

        cblock.console.getShowEvents = function() {return cblock.console.showevents;}


        /* Create context menu for the console */
        var console_top_ctx = new dijit.Menu(
        {
          targetNodeIds: [consoleTop.attr("id")]
        });

        console_top_ctx.addChild(new dijit.MenuItem({label:"Clear Console", onClick: function(){consoleTop.setContent("");}}));
        var console_scroll_chk = new dijit.CheckedMenuItem({label: "Autoscroll", checked: cblock.console.autoscroll, onChange: function(e){cblock.console.setAutoscroll(e);}});
        var console_events_chk = new dijit.CheckedMenuItem({label: "Show Events", checked: cblock.console.showevents, onChange: function(e){cblock.console.setShowEvents(e)}});

        console_top_ctx.addChild(console_scroll_chk);
        console_top_ctx.addChild(console_events_chk);

        consoleTabPane.context_menu = console_top_ctx;


        var updateContextMenu = function()
        {
           if (tabsContainer === undefined)
             return;

           var selected = tabsContainer.selected_tab;
           if (selected === undefined)
             return;

           var context = selected.context_menu;
           if (context === undefined)
           {
             $(context_menu.domNode).hide();
             return;
           }

           $(context_menu.domNode).show();

           context_menu.set("label", selected.attr("title"));
           context_menu.set("popup", context);
        }

        dojo.subscribe(tabsContainer.attr("id")+"-selectChild", function(child)
        {
          tabsContainer.selected_tab = child;
          updateContextMenu();
        });

        updateContextMenu();
  

        setInterval(function()
         {
            if (cblock.connected == false && !dlg.open)
            {
              dlg.show();
              $("#"+dlg.attr("id")+"_underlay").css("background-color", "gray");
            }

            time_txt.setContent(dojo.date.locale.format(new Date(),{formatLength:"medium"}));
           
         }, 500);


        var updateRibbonInfo = function(info)
        {
          if (info === undefined)
            return;

          if (info.name)
            server_txt.setContent(info.name);

          if (info.pcur !== undefined && info.pmax !== undefined)
            players_txt.setContent(info.pcur +"/"+info.pmax);

          if (info.mode)
            mode_txt.setContent(info.mode);

          if (info.map)
            map_txt.setContent(info.map);
 
          if (info.svers)
            version_txt.setContent(info.svers);

          if (info.rcur !== undefined && info.rmax !== undefined)
            rounds_txt.setContent(info.rcur +"/"+info.rmax);
         
        };
     

         /* client */

        var serverInfo_listener = function(result)
        {
          var words = result.data.slice(2);
          var sinfo =
           {
             name: words[0],
             pcur: words[1],
             pmax: words[2],
             mode: words[3],
             map: words[4],
             rcur: words[5],
             rmax: words[6]
           };
           updateRibbonInfo(sinfo);
        };

        var serverVersion_listener = function(result)
        {
          var words = result.data.slice(3);
          var svers = { svers: words[0]};
          updateRibbonInfo(svers);
        };


        client_start = function()
        {
          if (!cblock.connected)
            return;

          /* turn on events */
          send_packet({conid: cblock.conid, words: ["eventsEnabled", "true"]});

          /* count the players in the server */
          /*
          send_packet({conid: cblock.conid, words: ["admin.listPlayers", "all"]}, function(result)
           {

             var words = result.data.slice(2);
             var nparams = parseInt(words[0]);
             var nplayers = words[nparams+1];
             alert(nparams+" parameters, for "+nplayers+" players");
           });
          */
  
          /* do the first request, and start the loop */
          var sinfo_loop_interval = 10;
          send_packet({conid: cblock.conid, words:["serverInfo"]}, serverInfo_listener);
          cblock.sinfo_loop = setInterval(function()
           {
              if (!cblock.connected)
                return;
              send_packet({conid: cblock.conid, words:["serverInfo"]}, serverInfo_listener);
           }, sinfo_loop_interval*1000);

          /* request the server version */
          send_packet({conid: cblock.conid, words:["version"]}, serverVersion_listener);

          set_event_callback("*", cblock.console.event_listener);
        }


     });
    
